/*
 * 
 *
 *  
 *      
 */
#include "drv_i2c.h"
#include "delay.h"

#define TRUE   1
#define FALSE  0

#define I2C_SDA    GPIO_PinSource11
#define I2C_SCL    GPIO_PinSource10

// SDA on port A, bit 9                             // 适配实际硬件
#define SDA_LOW()  (GPIO_ResetBits(GPIOB, GPIO_Pin_11))     // set SDA to low
#define SDA_OPEN() (GPIO_SetBits(GPIOB, GPIO_Pin_11))     // set SDA to open-drain
#define SDA_READ() (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11))     // read SDA

// SCL on port A, bit 8                             // 适配实际硬件
#define SCL_LOW()  (GPIO_ResetBits(GPIOB, GPIO_Pin_10))     // set SCL to low
#define SCL_OPEN() (GPIO_SetBits(GPIOB, GPIO_Pin_10))     // set SCL to open-drain
#define SCL_READ() (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10))     // read SCL

void i2c_delay_us(uint32_t t)
{
    uint32_t cnt = ((SystemCoreClock >> 20) * t * 11) >> 6;
    while(cnt--);
}
uint32_t loop;

void drv_i2c_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    /* Configure pins: SCL */
    GPIO_InitStructure.GPIO_Pin = (1<<I2C_SDA);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* Configure pins: SDA */
    GPIO_InitStructure.GPIO_Pin = (1<<I2C_SCL);
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    SDA_OPEN();                  // I2C-bus 空闲模式 SDA 为高
    SCL_OPEN();                  // I2C-bus 空闲模式 SCL 为高

}

void drv_i2c_start(void)
{
    SDA_OPEN();
    i2c_delay_us(I2C_PERIOD);
    SCL_OPEN();
    i2c_delay_us(I2C_PERIOD);
    SDA_LOW();
    i2c_delay_us(2*I2C_PERIOD);   // hold time start condition (t_HD;STA)
    SCL_LOW();
    i2c_delay_us(2*I2C_PERIOD);
}

void drv_i2c_stop (void)
{
    SCL_LOW();
    i2c_delay_us(I2C_PERIOD);
    SDA_LOW();
    i2c_delay_us(I2C_PERIOD);
    SCL_OPEN();
    i2c_delay_us(2*I2C_PERIOD);   // set-up time stop condition (t_SU;STO)
    SDA_OPEN();
    i2c_delay_us(2*I2C_PERIOD);
}

int8_t drv_i2c_writebyte (uint8_t txByte)
{
    uint8_t mask;
    int32_t error = ERR_NONE;
    for (mask = 0x80; mask > 0; mask >>= 1)   // shift bit for masking (8 times)
    {
        if ((mask & txByte) == 0)
            SDA_LOW();   // masking txByte, write bit to SDA-Line
        else
            SDA_OPEN();

        i2c_delay_us(I2C_PERIOD);               // data set-up time (t_SU;DAT)
        SCL_OPEN();                                 // generate clock pulse on SCL
        i2c_delay_us(2*I2C_PERIOD);             // SCL high time (t_HIGH)
        SCL_LOW();
        i2c_delay_us(I2C_PERIOD);               // data hold time(t_HD;DAT)
    }
    SDA_OPEN();                             // release SDA-line
    SCL_OPEN();                             // clk #9 for ack
    i2c_delay_us(I2C_PERIOD);           // data set-up time (t_SU;DAT)

    if (SDA_READ())
        error = ERR_ACK;                    // check ack from i2c slave

    i2c_delay_us(I2C_PERIOD);           // data set-up time (t_SU;DAT)
    SCL_LOW();
    i2c_delay_us(4*I2C_PERIOD);         // wait to see byte package on scope
    return error;                           // return error code
}

uint8_t drv_i2c_readbyte (uint8_t ackflag)
{
    uint8_t mask;
    uint8_t rxByte = 0;
    SDA_OPEN();                             // release SDA-line
    i2c_delay_us(2*I2C_PERIOD);       // SCL high time (t_HIGH)
    for (mask = 0x80; mask > 0; mask >>= 1) // shift bit for masking (8 times)
    {
        SCL_OPEN();                         // start clock on SCL-line
        //i2c_delay_us(3);              // data set-up time (t_SU;DAT)
        while (SCL_READ() == FALSE)
            ;
        i2c_delay_us(4*I2C_PERIOD);       // SCL high time (t_HIGH)
        if (SDA_READ())
            rxByte = rxByte | mask;         // read bit
        SCL_LOW();
        i2c_delay_us(4*I2C_PERIOD);     // data hold time(t_HD;DAT)
    }
    if (ackflag)
        SDA_LOW();                          // send acknowledge if necessary
    else
        SDA_OPEN();

    i2c_delay_us(I2C_PERIOD);               // data set-up time (t_SU;DAT)
    SCL_OPEN();                             // clk #9 for ack
    i2c_delay_us(2*I2C_PERIOD);         // SCL high time (t_HIGH)
    SCL_LOW();
    SDA_OPEN();                             // release SDA-line
    i2c_delay_us(4*I2C_PERIOD);         // wait to see byte package on scope
    return rxByte;                          // return error code
}

uint8_t drv_i2c_select_dev (uint8_t addr, uint8_t optype)
{
    return drv_i2c_writebyte (addr | (optype & 0x1));
}

